﻿<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Javascript Generic List Implementation</title>
    <script type="text/javascript" src="NetJS_CLR.js"></script>
    <script type="text/javascript" src="linq.js"></script>
    <!-- <script type="text/javascript" src="linq.rx.js"></script>  -->
    <!-- <script type="text/javascript" src="Reflection.js"></script> -->
    <!-- <script type="text/javascript" src="GenericList.js"></script> -->
    <script type="text/javascript">        

        //Classes

        //Define a class of car which we will store in the list later on.
        function Car(make, model) {
            //Could force the return of $default here or return null/undefined if make or model is missing
            this.make = make;
            this.model = model;
        }

        //Specify default value, could be anything
        Car.$default = new Car();

        //Unit Tests

        function TestSystem() {
            var test = new System.String();
            if (!System.Diagnostics.Assert(test, typeof undefined)) throw('Wrong Return');

            if (!Default(System.String).toString() === typeof undefined) throw ('Wrong Return');

            if (Default(null) === typeof undefined) throw ('Wrong Return');

            if (!Default(Number) === 0) throw ('Wrong Return');

            if (!Default(String) === System.String.Empty.toString()) throw ('Wrong Return');

            if (!Default(Boolean) === false) throw ('Wrong Return');

        }

        function TestReflection() {
            var myList = new List(),
                test = Reflection.getArguments(myList.Add);
            if (!System.Diagnostics.Assert(test, ['object'])) throw('Wrong Return');

            test = Reflection.getArguments(myList.Reverse);
            if (!System.Diagnostics.Assert(test, ['index', 'count'])) throw('Wrong Return');
        }

        function TestCast() {
            var test = new myClass(),
            anotherTest = new anotherClass(),
            composed = test + anotherTest,
            yaComposed = test.cast(Number, function () {
                return this + anotherTest
            });
            if (!System.Diagnostics.Assert(composed, 1)) throw('Wrong Return');  //Because both types overload to return numbers which should equal 1 by default 0 + 1 = 1;
            if (!System.Diagnostics.Assert(yaComposed, 1)) throw ('Wrong Return'); //Because test Casted as a Number return 0 added to the derived class which overloads + to return a number which is default 1;
            
            //Should fail because anotherClass hides the function cast
            try {
                yaCComposed = anotherTest.cast(Number, function () {
                    return this + test;
                });                
            } catch (ex) { console.log(ex); }

            //Should not fail because anotherClass is an instance of myClass
            t = test.cast(anotherClass, function () {
                return this + anotherTest
            });

            if (!System.Diagnostics.Assert(t, 2)) throw ('Wrong Return');  //Because test is casted to anotherClass which gives it a default value of 1 and 1 + 1 = 2;

            //Should fail because anotherClass hides the function cast
            try {
                tt = anotherTest.cast(myClass, function () {
                    return this + test;
                });
            } catch (ex) { console.log(ex); }

            //Should fail because baseClass is abstract
            try { new baseClass(); }
            catch (e) {console.log(e); }

        }

        function TestList() {

            var myList = new List();
            myList.Add(new Car("Honda", "Civic"));
            myList.Add(new Car("Nissan", "Sentra"));
            myList.Add(new Car("Honda", "Cr-V"));
            myList.Add(new Car("Honda", "Cr-V"));

            //This will retrieve the 0th member from the List
            var testGetter = myList[0]; // Car{make:'Honda',model:'Civic'}

            try {
                //This will produce an exception
                var testGetterException = myList[9];
            }
            catch (_) {
                //_ = "index parameter out of range in List.Get"
                console.log(_);
            }

            var aFirst = myList.FirstOrDefault(function (c) { return c.make === 'Nissan'; });

            var anotherStringForm = myList.FirstOrDefault('c => c.make == "Nissan"'); // 1 in Count

            anotherStringForm = myList.FirstOrDefault('(c) => c.make == "Nissan"'); // 1 in Count

            anotherStringForm = myList.Where("(c) => c.make == 'Nissan' ? new Car('Acura', 'TL') : Car.$default"); // 4 in Count, 1 Acura and 3 Default Cars

            if (!System.Diagnostics.Assert(anotherStringForm[1], new Car('Acura', 'TL'))) alert('Bug');

            anotherStringForm = myList.Where(function (c) { return c.make == 'Nissan' ? new Car('Acura', 'TL') : Car.$default });  // 4 in Count, 1 Acura and 3 Default Cars

            if (!System.Diagnostics.Assert(anotherStringForm[1], new Car('Acura', 'TL'))) alert('Bug');

            var aDefault = myList.FirstOrDefault(function (c) { c.make === 'Nissan'; }); //Accidently left the return off so there is an undefined result

            if (!System.Diagnostics.Assert(anotherStringForm[1], Car.$default)) alert('Bug');

            var selList = myList.Where("make == 'Honda'").OrderByDescending("model").Distinct(); //2 in Count
            var anotherList = myList.Where(function () { return this.make == 'Honda' }).OrderByDescending("model").Distinct(); //2 in Count
            var yetAnotherList = myList.Where(function (c) { return c.make == 'Honda' }).OrderByDescending("model").Distinct(); //2 in Count
            var finalList = myList.Where(function () { return make == 'Honda' }).OrderByDescending("model").Distinct(); //2 in Count

            if (selList.Count() !== 2 || anotherList.Count() !== 2 || yetAnotherList.Count() !== 2 || finalList.Count() !== 2) alert('Bug');

            var outputString = '';
            for (var index = 0; index < selList.Count(); index++) {
                outputString += selList.ElementAt(index).model + '\r\n';
            }

            var outputElement = document.getElementById('output');
            if(outputElement) if (outputElement.textContent) outputElement.textContent = outputString;
            else if (outputElement.innerText) outputElement.innerText = outputString;
            delete outputElement;
        }

        function TestDictionary() {

            var myDictionary = new Dictionary(String, Number);

            myDictionary.Add('Test', 0);

            myDictionary.Add('Test1', 1);

            myDictionary.Add('Test2', 2);

            myDictionary.Add('VA', 240);

            if (!System.Diagnostics.Assert(myDictionary.Count(), 4)) alert('Bug');

            if (!System.Diagnostics.Assert(myDictionary['VA'], 240)) alert('Bug');

            if (!System.Diagnostics.Assert(myDictionary['Test'], 0)) alert('Bug');

            if (!System.Diagnostics.Assert(myDictionary['Test1'], 1)) alert('Bug');

            if (!System.Diagnostics.Assert(myDictionary['Test1'], 2)) alert('Bug');

            myDictionary['Test'] = myDictionary['Test1'];

            if (!System.Diagnostics.Assert(myDictionary['Test'], 1)) alert('Bug');

            //Should not add
            try { myDictionary.Add({}, 'Test'); } //Should Eventually be a DictionaryException
            catch (_) { if (_ !== 'Only one object type is allowed in a list') alert('Bug'); }

            //Should not modify
            try {
                myDictionary['Test'] = {};
                if (!System.Diagnostics.Assert(myDictionary['Test'], 1)) alert('Bug');
            } //Should Eventually be a DictionaryException
            catch (_) { if (_ !== 'Only one object type is allowed in a list') alert('Bug'); }

            if (!System.Diagnostics.Assert(myDictionary.Count(), 4)) alert('Bug');

        }

        function init()
        {
            try
            {

            (function (_window) {

                TestList();

                TestDictionary();

                TestReflection();

                TestCast();
                
                TestSystem();

            }).bind(window.CLR)(this);

                
            }
            catch(ex)
            {
                alert('The following error occurred: ' + ex);
                debugger;
                throw ex;
            }
        }
    
    </script>

</head>
<body onload="init()">
    <h1 id="output" runat="server" />
</body>
</html>
